const fs = require('fs');
const path = require('path');
const rq = require('request-promise');
const prettier = require('prettier');
const prettierOptions = require('./.prettierrc.js');
const ROOT_URL = 'http://192.168.1.136:8000';
const API_URL = `${ROOT_URL}/api`;
const PROJECT_ID = 332;
const getUrl = path => `${API_URL}${path}`;
const COOKIE_JAR = rq.jar();

function makeTransformFirstChar(fn) {
  return function transformStr(s) {
    if (typeof s !== 'string') return s;
    const head = fn(s.charAt(0));
    const tail = s.slice(1);
    return `${head}${tail}`;
  };
}

const upperCaseFirstChar = makeTransformFirstChar(c => c.toUpperCase());
const lowerCaseFirstChar = makeTransformFirstChar(c => c.toLowerCase());

function makeCharToUpperReplacer(replaceStr) {
  const replaceStringList = Array.isArray(replaceStr)
    ? replaceStr
    : [replaceStr];
  const regStr = replaceStringList.map(c => `([${c}]+[^${c}])`).join('|');
  const reg = new RegExp(regStr, 'g');

  function charToUpper(string) {
    if (typeof string !== 'string' || !string) return string;
    // eslint-disable-next-line
    return string.replace(reg, (_, ...rest) => {
      for (let i = 0; i < rest.length; i += 1) {
        const c = rest[i];
        if (c !== undefined) {
          const replaceChar = replaceStringList[i];
          return charToUpper(upperCaseFirstChar(c.replace(replaceChar, '')));
        }
      }
      return _;
    });
  }

  return charToUpper;
}

const upperCaseUnderSlash = makeCharToUpperReplacer(['_', '/']);

function lowerCaseUnderSlashLearChar(s) {
  return lowerCaseFirstChar(upperCaseUnderSlash(s));
}

function setupCookie(response) {
  const json = JSON.parse(response.body);
  const { uid } = json.data;
  const uidCookie = `_yapi_uid=${uid}`;
  const arr = response.headers['set-cookie'];
  const target = arr.find(t => t.match(/^_yapi_token=/));

  if (target) {
    const match = target.match(/^(_yapi_token=[^;]+);/);
    if (match) {
      const tokenCookie = match[1];
      COOKIE_JAR.setCookie(tokenCookie, ROOT_URL);
    }
  }

  COOKIE_JAR.setCookie(uidCookie, ROOT_URL);
}

async function login() {
  const username = 'tourist@yapi.com';
  const password = '123456';
  const response = await rq({
    uri: getUrl('/user/login'),
    method: 'POST',
    body: JSON.stringify({ email: username, password }),
    headers: {
      'content-type': 'application/json;charset=UTF-8'
    },
    simple: false,
    resolveWithFullResponse: true
  });
  if (response.statusCode === 200) {
    const data = JSON.parse(response.body);
    if (!data.errcode) {
      setupCookie(response);
    } else {
      throw new Error(data.errmsg);
    }
  } else {
    throw new Error(response.body);
  }
}

async function fetchInterfaces() {
  const res = JSON.parse(
    await rq({
      uri: getUrl('/interface/list'),
      jar: COOKIE_JAR,
      qs: { page: 1, limit: 1000, project_id: PROJECT_ID }
    })
  );
  if (!res.errcode) {
    return res.data.list;
  }
  throw new Error(res.errmsg);
}

async function fetchDetail(id) {
  const res = JSON.parse(
    await rq({
      uri: getUrl('/interface/get'),
      jar: COOKIE_JAR,
      qs: { id }
    })
  );
  if (!res.data.errcode) {
    return res.data;
  }
  throw new Error(res.data.errmsg);
}

async function fetchDetails(ids) {
  const batch = 10;
  const getDetails = async (list, result) => {
    if (!list.length) return result;
    const batchList = await Promise.all(list.slice(0, batch).map(fetchDetail));
    return await getDetails(list.slice(batch), result.concat(batchList));
  };
  return await getDetails(ids, []);
}

function sortInterfaces(list) {
  return list.map(t => t).sort((a, b) => (a.path < b.path ? 0 : 1));
}

function itob(n) {
  return `${n}` === '1';
}

function yesOrNo(n) {
  return itob(n) ? '是' : '否';
}

function makeListGenerator(key, prop, fn) {
  const getData = typeof key === 'function' ? key : data => data[key];
  return function generateCode(data) {
    const d = getData(data);
    if (!Array.isArray(d) || !d.length) return '';
    const str = d.map(fn).join(',');
    if (!str) return '';
    return `${prop}: { ${str} }`;
  };
}

function escapeBreakLine(s) {
  return `${s}`.replace(/\r?\n/ig, ' ');
}

function filterJsonContentTypeHeader(list) {
  const isContentType = s => s.toLowerCase() === 'content-type';
  const isJsonType = s => /application\/json/i.test(s);
  const isJsonItem = t => !(isContentType(t.name) && isJsonType(t.value));
  return list.filter(isJsonItem);
}

const generateQuery = makeListGenerator(
  'req_query',
  'params',
  t => `
// ${escapeBreakLine(t.desc)}, required: ${yesOrNo(t.required)}
"${t.name}": ""`
);

const generateHeaders = makeListGenerator(
  d => filterJsonContentTypeHeader(d.req_headers),
  'headers',
  t => `
"${t.name}": "${t.value}"
`
);

const generatePostBody = makeListGenerator(
  'req_body_form',
  'data',
  t => `
// ${escapeBreakLine(t.desc)}, type: ${t.type}, required: ${yesOrNo(t.required)}
"${t.name}": ""`
);

function generatePath(t) {
  return `path: "${t.path}"`;
}

function generateMethhod(t) {
  if (t.method.toLowerCase() === 'get') return '';
  return `method: "${t.method}"`;
}

function generateApi(data) {
  const generators = [
    generatePath,
    generateMethhod,
    generateHeaders,
    generateQuery,
    generatePostBody
  ];
  return `
// ${data.title}, ${lowerCaseUnderSlashLearChar(data.path)}
{
  ${generators
    .map(fn => fn(data))
    .filter(t => t)
    .join(',')}
}`;
}

function generateCode(list) {
  const code = `
/* eslint-disable max-len */
/*
  此文件为使用 'npm run build:api' 时自动生成的文件，请不要在该文件中手动添加，
  如需手动添加接口，请在其他文件中添加
  接口项目地址; ${ROOT_URL}/project/${PROJECT_ID}/interface/api
 */

export default [
  ${list.map(generateApi).join(',\n')}
]`;
  return prettier.format(code, { ...prettierOptions, parser: 'babel' });
}

async function main() {
  await login();
  const interfaces = await fetchInterfaces();
  const detailList = await fetchDetails(
    sortInterfaces(interfaces).map(t => t._id)
  );
  const code = generateCode(detailList);
  fs.writeFileSync(
    path.join(__dirname, 'api', 'definition', 'dapp_doc.js'),
    code
  );
  console.log(`生成成功.共${interfaces.length}个接口`);
}

main();
